package dev.ece.suit.defer;

import java.io.InputStream;
import java.util.Map;

import org.lwjgl.opengl.GL20;

import dev.ece.core.cells.world.ShadowInfo;
import dev.ece.core.cells.world.ShadowInfoLocationHandler;
import dev.ece.core.fog.Fog;
import dev.ece.core.fog.FogLocationHandler;
import dev.ece.core.light.Light;
import dev.ece.core.light.Lights;
import dev.ece.core.light.LightLocationHandler;
import dev.ece.core.light.Material;
import dev.ece.core.shader.Shader;
import dev.ece.core.texture.FBO;
import dev.ece.core.texture.FBOS;
import dev.ece.suit.utils.ShaderFunctions;
import dev.ece.util.tools.Camera;

public class DeferRenderShader extends Shader {
	
	public static final int MAX_LIGHT_COUNT = 32;

	private static DeferRenderShader defaultDeferRenderShader;
	
	public static DeferRenderShader getDefaultDeferRenderShader() {
		if(defaultDeferRenderShader == null) {
			defaultDeferRenderShader = new DeferRenderShader(
					DeferRenderShader.class.getClassLoader().getResourceAsStream("shaders/defer/render.vert"), 
					DeferRenderShader.class.getClassLoader().getResourceAsStream("shaders/defer/render.frag"), 
					ShaderFunctions.getFunctions());
		}
		return defaultDeferRenderShader;
	}

	public DeferRenderShader(InputStream vertexShaderInputStream,
			InputStream pixelShaderInputStream, Map<String, String> functions) {
		super(vertexShaderInputStream, pixelShaderInputStream, functions);
	}

	@Override
	protected String getVertexHeader() {
		StringBuffer s = new StringBuffer();
		s.append(Light.getLightStructString());
		s.append(Material.getMaterialStructString());
		s.append(Fog.getFogStructString());
		s.append(ShadowInfo.getShadowInfoStructString());
		s.append(super.getVertexHeader());
		return s.toString();
	}

	@Override
	protected String getPixelHeader() {
		return this.getVertexHeader();
	}

	private LightLocationHandler[] lightLocationHandlers;
	
	private FogLocationHandler fogLocationHandler;
	
	private int eceGlPositionHandler;
	
	private int eceGlNormalHandler;
	
	private int eceGlAlbedoSpecHandler;
	
	private int eceDecalHandler;
	
	private int cameraPositionHandler;
	
	private ShadowInfoLocationHandler shadowInfoLocationHandler;
	
	@Override
	protected void onBindAttribLocations(int program) {
		GL20.glBindAttribLocation(program, 0, "ece_Vertex");
		GL20.glBindAttribLocation(program, 1, "ece_TexCoord");
		GL20.glBindAttribLocation(program, 2, "ece_Normal");
	}

	@Override
	protected void onInitializeHandler(int program) {
		lightLocationHandlers = new LightLocationHandler[MAX_LIGHT_COUNT];
		for(int i=0; i<Light.MAX_LIGHT_COUNT; i++) {
			lightLocationHandlers[i] = LightLocationHandler.newInstance(program, "light[" + i + "].");
		}
		fogLocationHandler = FogLocationHandler.newInstance(program, "fog.");
		eceGlPositionHandler = GL20.glGetUniformLocation(program, "eceGlPosition");
		eceGlNormalHandler = glGetUniformLocation("eceGlNormal");
		eceGlAlbedoSpecHandler = glGetUniformLocation("eceGlAlbedoSpec");
		eceDecalHandler = glGetUniformLocation("eceDecal");
		cameraPositionHandler = GL20.glGetUniformLocation(program, "viewPosition");
		shadowInfoLocationHandler = ShadowInfoLocationHandler.newInstance(program, "shadow.");
	}

	public void glDeferHandler(FBOS defer) {
		GL20.glUniform1i(eceGlPositionHandler, defer.getTextures()[0].bind(1));
		GL20.glUniform1i(eceGlNormalHandler, defer.getTextures()[1].bind(2));
		GL20.glUniform1i(eceGlAlbedoSpecHandler, defer.getTextures()[2].bind(3));
	}
	
	public void glDecalHandler(FBO decal) {
		GL20.glUniform1i(eceDecalHandler, decal.getTexture().bind(4));
	}
	
	public void glShadowInfo(ShadowInfo shadowInfo) {
		shadowInfoLocationHandler.glUniformShadowInfo(shadowInfo, 0);
	}
	
	public void glCamera(Camera camera) {
		camera.position.glUniform3f(cameraPositionHandler);
	}
	
	@Override
	public void glUseProgram() {
		super.glUseProgram();
		//灯光信息
		Lights.glUniformLights(lightLocationHandlers);
		//雾化信息
		Fog.getSysInstance().glUniform(fogLocationHandler);
	}

	@Override
	public void clearup() {
		for(int i=0; i<Light.MAX_LIGHT_COUNT; i++) {
			lightLocationHandlers[i].clearup();
		}
		fogLocationHandler.clearup();
		shadowInfoLocationHandler.clearup();
		GL20.glDeleteShader(eceGlPositionHandler);
		GL20.glDeleteShader(eceGlNormalHandler);
		GL20.glDeleteShader(eceGlAlbedoSpecHandler);
		GL20.glDeleteShader(cameraPositionHandler);
		super.clearup();
		fogLocationHandler = null;
		shadowInfoLocationHandler = null;
	}

}
